simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

operators.js (7673B)


      1 "use strict";
      2 
      3 Object.defineProperty(exports, "__esModule", {
      4   value: true
      5 });
      6 exports.getAssociativity = getAssociativity;
      7 exports.getPrecedence = getPrecedence;
      8 exports.isAssociativeWith = isAssociativeWith;
      9 exports.properties = void 0;
     10 
     11 var _object = require("../utils/object.js");
     12 
     13 // list of identifiers of nodes in order of their precedence
     14 // also contains information about left/right associativity
     15 // and which other operator the operator is associative with
     16 // Example:
     17 // addition is associative with addition and subtraction, because:
     18 // (a+b)+c=a+(b+c)
     19 // (a+b)-c=a+(b-c)
     20 //
     21 // postfix operators are left associative, prefix operators
     22 // are right associative
     23 //
     24 // It's also possible to set the following properties:
     25 // latexParens: if set to false, this node doesn't need to be enclosed
     26 //              in parentheses when using LaTeX
     27 // latexLeftParens: if set to false, this !OperatorNode's!
     28 //                  left argument doesn't need to be enclosed
     29 //                  in parentheses
     30 // latexRightParens: the same for the right argument
     31 var properties = [{
     32   // assignment
     33   AssignmentNode: {},
     34   FunctionAssignmentNode: {}
     35 }, {
     36   // conditional expression
     37   ConditionalNode: {
     38     latexLeftParens: false,
     39     latexRightParens: false,
     40     latexParens: false // conditionals don't need parentheses in LaTeX because
     41     // they are 2 dimensional
     42 
     43   }
     44 }, {
     45   // logical or
     46   'OperatorNode:or': {
     47     associativity: 'left',
     48     associativeWith: []
     49   }
     50 }, {
     51   // logical xor
     52   'OperatorNode:xor': {
     53     associativity: 'left',
     54     associativeWith: []
     55   }
     56 }, {
     57   // logical and
     58   'OperatorNode:and': {
     59     associativity: 'left',
     60     associativeWith: []
     61   }
     62 }, {
     63   // bitwise or
     64   'OperatorNode:bitOr': {
     65     associativity: 'left',
     66     associativeWith: []
     67   }
     68 }, {
     69   // bitwise xor
     70   'OperatorNode:bitXor': {
     71     associativity: 'left',
     72     associativeWith: []
     73   }
     74 }, {
     75   // bitwise and
     76   'OperatorNode:bitAnd': {
     77     associativity: 'left',
     78     associativeWith: []
     79   }
     80 }, {
     81   // relational operators
     82   'OperatorNode:equal': {
     83     associativity: 'left',
     84     associativeWith: []
     85   },
     86   'OperatorNode:unequal': {
     87     associativity: 'left',
     88     associativeWith: []
     89   },
     90   'OperatorNode:smaller': {
     91     associativity: 'left',
     92     associativeWith: []
     93   },
     94   'OperatorNode:larger': {
     95     associativity: 'left',
     96     associativeWith: []
     97   },
     98   'OperatorNode:smallerEq': {
     99     associativity: 'left',
    100     associativeWith: []
    101   },
    102   'OperatorNode:largerEq': {
    103     associativity: 'left',
    104     associativeWith: []
    105   },
    106   RelationalNode: {
    107     associativity: 'left',
    108     associativeWith: []
    109   }
    110 }, {
    111   // bitshift operators
    112   'OperatorNode:leftShift': {
    113     associativity: 'left',
    114     associativeWith: []
    115   },
    116   'OperatorNode:rightArithShift': {
    117     associativity: 'left',
    118     associativeWith: []
    119   },
    120   'OperatorNode:rightLogShift': {
    121     associativity: 'left',
    122     associativeWith: []
    123   }
    124 }, {
    125   // unit conversion
    126   'OperatorNode:to': {
    127     associativity: 'left',
    128     associativeWith: []
    129   }
    130 }, {
    131   // range
    132   RangeNode: {}
    133 }, {
    134   // addition, subtraction
    135   'OperatorNode:add': {
    136     associativity: 'left',
    137     associativeWith: ['OperatorNode:add', 'OperatorNode:subtract']
    138   },
    139   'OperatorNode:subtract': {
    140     associativity: 'left',
    141     associativeWith: []
    142   }
    143 }, {
    144   // multiply, divide, modulus
    145   'OperatorNode:multiply': {
    146     associativity: 'left',
    147     associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'Operator:dotMultiply', 'Operator:dotDivide']
    148   },
    149   'OperatorNode:divide': {
    150     associativity: 'left',
    151     associativeWith: [],
    152     latexLeftParens: false,
    153     latexRightParens: false,
    154     latexParens: false // fractions don't require parentheses because
    155     // they're 2 dimensional, so parens aren't needed
    156     // in LaTeX
    157 
    158   },
    159   'OperatorNode:dotMultiply': {
    160     associativity: 'left',
    161     associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'OperatorNode:dotMultiply', 'OperatorNode:doDivide']
    162   },
    163   'OperatorNode:dotDivide': {
    164     associativity: 'left',
    165     associativeWith: []
    166   },
    167   'OperatorNode:mod': {
    168     associativity: 'left',
    169     associativeWith: []
    170   }
    171 }, {
    172   // unary prefix operators
    173   'OperatorNode:unaryPlus': {
    174     associativity: 'right'
    175   },
    176   'OperatorNode:unaryMinus': {
    177     associativity: 'right'
    178   },
    179   'OperatorNode:bitNot': {
    180     associativity: 'right'
    181   },
    182   'OperatorNode:not': {
    183     associativity: 'right'
    184   }
    185 }, {
    186   // exponentiation
    187   'OperatorNode:pow': {
    188     associativity: 'right',
    189     associativeWith: [],
    190     latexRightParens: false // the exponent doesn't need parentheses in
    191     // LaTeX because it's 2 dimensional
    192     // (it's on top)
    193 
    194   },
    195   'OperatorNode:dotPow': {
    196     associativity: 'right',
    197     associativeWith: []
    198   }
    199 }, {
    200   // factorial
    201   'OperatorNode:factorial': {
    202     associativity: 'left'
    203   }
    204 }, {
    205   // matrix transpose
    206   'OperatorNode:transpose': {
    207     associativity: 'left'
    208   }
    209 }];
    210 /**
    211  * Get the precedence of a Node.
    212  * Higher number for higher precedence, starting with 0.
    213  * Returns null if the precedence is undefined.
    214  *
    215  * @param {Node} _node
    216  * @param {string} parenthesis
    217  * @return {number | null}
    218  */
    219 
    220 exports.properties = properties;
    221 
    222 function getPrecedence(_node, parenthesis) {
    223   var node = _node;
    224 
    225   if (parenthesis !== 'keep') {
    226     // ParenthesisNodes are only ignored when not in 'keep' mode
    227     node = _node.getContent();
    228   }
    229 
    230   var identifier = node.getIdentifier();
    231 
    232   for (var i = 0; i < properties.length; i++) {
    233     if (identifier in properties[i]) {
    234       return i;
    235     }
    236   }
    237 
    238   return null;
    239 }
    240 /**
    241  * Get the associativity of an operator (left or right).
    242  * Returns a string containing 'left' or 'right' or null if
    243  * the associativity is not defined.
    244  *
    245  * @param {Node} _node
    246  * @param {string} parenthesis
    247  * @return {string|null}
    248  * @throws {Error}
    249  */
    250 
    251 
    252 function getAssociativity(_node, parenthesis) {
    253   var node = _node;
    254 
    255   if (parenthesis !== 'keep') {
    256     // ParenthesisNodes are only ignored when not in 'keep' mode
    257     node = _node.getContent();
    258   }
    259 
    260   var identifier = node.getIdentifier();
    261   var index = getPrecedence(node, parenthesis);
    262 
    263   if (index === null) {
    264     // node isn't in the list
    265     return null;
    266   }
    267 
    268   var property = properties[index][identifier];
    269 
    270   if ((0, _object.hasOwnProperty)(property, 'associativity')) {
    271     if (property.associativity === 'left') {
    272       return 'left';
    273     }
    274 
    275     if (property.associativity === 'right') {
    276       return 'right';
    277     } // associativity is invalid
    278 
    279 
    280     throw Error('\'' + identifier + '\' has the invalid associativity \'' + property.associativity + '\'.');
    281   } // associativity is undefined
    282 
    283 
    284   return null;
    285 }
    286 /**
    287  * Check if an operator is associative with another operator.
    288  * Returns either true or false or null if not defined.
    289  *
    290  * @param {Node} nodeA
    291  * @param {Node} nodeB
    292  * @param {string} parenthesis
    293  * @return {boolean | null}
    294  */
    295 
    296 
    297 function isAssociativeWith(nodeA, nodeB, parenthesis) {
    298   // ParenthesisNodes are only ignored when not in 'keep' mode
    299   var a = parenthesis !== 'keep' ? nodeA.getContent() : nodeA;
    300   var b = parenthesis !== 'keep' ? nodeA.getContent() : nodeB;
    301   var identifierA = a.getIdentifier();
    302   var identifierB = b.getIdentifier();
    303   var index = getPrecedence(a, parenthesis);
    304 
    305   if (index === null) {
    306     // node isn't in the list
    307     return null;
    308   }
    309 
    310   var property = properties[index][identifierA];
    311 
    312   if ((0, _object.hasOwnProperty)(property, 'associativeWith') && property.associativeWith instanceof Array) {
    313     for (var i = 0; i < property.associativeWith.length; i++) {
    314       if (property.associativeWith[i] === identifierB) {
    315         return true;
    316       }
    317     }
    318 
    319     return false;
    320   } // associativeWith is not defined
    321 
    322 
    323   return null;
    324 }