Annotation of loncom/html/adm/LC_math_editor/src/definitions.js, revision 1.2

1.1       damieng     1: /*
                      2: 
                      3: Copyright (C) 2014  Michigan State University Board of Trustees
                      4: 
                      5: The JavaScript code in this page is free software: you can
                      6: redistribute it and/or modify it under the terms of the GNU
                      7: General Public License (GNU GPL) as published by the Free Software
                      8: Foundation, either version 3 of the License, or (at your option)
                      9: any later version.  The code is distributed WITHOUT ANY WARRANTY;
                     10: without even the implied warranty of MERCHANTABILITY or FITNESS
                     11: FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
                     12: 
                     13: As additional permission under GNU GPL version 3 section 7, you
                     14: may distribute non-source (e.g., minimized or compacted) forms of
                     15: that code without the copy of the GNU GPL normally required by
                     16: section 4, provided you include this license notice and a URL
                     17: through which recipients can access the Corresponding Source.
                     18: 
                     19: */
                     20: 
1.2     ! damieng    21: "use strict";
        !            22: 
1.1       damieng    23: /**
                     24:  * Operator definitions (see function define() at the end).
                     25:  * @constructor
                     26:  */
                     27: function Definitions() {
                     28:     this.operators = [];  /* Array of Operator */
                     29: }
                     30: 
1.2     ! damieng    31: Definitions.ARG_SEPARATOR = ",";
1.1       damieng    32: Definitions.DECIMAL_SIGN_1 = ".";
1.2     ! damieng    33: Definitions.DECIMAL_SIGN_2 = ".";
        !            34: Definitions.INTERVAL_SEPARATOR = ":";
1.1       damieng    35: 
                     36: /**
                     37:  * Creates a new operator.
                     38:  * @param {string} id - Operator id (text used to recognize it)
                     39:  * @param {number} arity - Operator.UNARY, BINARY or TERNARY
                     40:  * @param {number} lbp - Left binding power
                     41:  * @param {number} rbp - Right binding power
                     42:  * @param {function} nud - Null denotation function
                     43:  * @param {function} led - Left denotation function
                     44:  */
                     45: Definitions.prototype.operator = function(id, arity, lbp, rbp, nud, led) {
                     46:     this.operators.push(new Operator(id, arity, lbp, rbp, nud, led));
                     47: };
                     48: 
                     49: /**
                     50:  * Creates a new separator operator.
                     51:  * @param {string} id - Operator id (text used to recognize it)
                     52:  */
                     53: Definitions.prototype.separator = function(id) {
                     54:     this.operator(id, Operator.BINARY, 0, 0, null, null);
                     55: };
                     56: 
                     57: /**
                     58:  * Creates a new infix operator.
                     59:  * @param {string} id - Operator id (text used to recognize it)
                     60:  * @param {number} lbp - Left binding power
                     61:  * @param {number} rbp - Right binding power
                     62:  * @param {ledFunction} [led] - Left denotation function
                     63:  */
                     64: Definitions.prototype.infix = function(id, lbp, rbp, led) {
                     65:     var arity, nud;
                     66:     arity = Operator.BINARY;
                     67:     nud = null;
                     68:     led = led || function(p, left) {
                     69:         var children = [left, p.expression(rbp)];
                     70:         return new ENode(ENode.OPERATOR, this, id, children);
                     71:     };
                     72:     this.operator(id, arity, lbp, rbp, nud, led);
                     73: };
                     74: 
                     75: /**
                     76:  * Creates a new prefix operator.
                     77:  * @param {string} id - Operator id (text used to recognize it)
                     78:  * @param {number} rbp - Right binding power
                     79:  * @param {nudFunction} [nud] - Null denotation function
                     80:  */
                     81: Definitions.prototype.prefix = function(id, rbp, nud) {
                     82:     var arity, lbp, led;
                     83:     arity = Operator.UNARY;
                     84:     lbp = 0;
                     85:     nud = nud || function(p) {
                     86:         var children = [p.expression(rbp)];
                     87:         return new ENode(ENode.OPERATOR, this, id, children);
                     88:     };
                     89:     led = null;
                     90:     this.operator(id, arity, lbp, rbp, nud, led);
                     91: };
                     92: 
                     93: /**
                     94:  * Creates a new suffix operator.
                     95:  * @param {string} id - Operator id (text used to recognize it)
                     96:  * @param {number} lbp - Left binding power
                     97:  * @param {ledFunction} [led] - Left denotation function
                     98:  */
                     99: Definitions.prototype.suffix = function(id, lbp, led) {
                    100:     var arity, rbp, nud;
                    101:     arity = Operator.UNARY;
                    102:     rbp = 0;
                    103:     nud = null;
                    104:     led = led || function(p, left) {
                    105:         var children = [left];
                    106:         return new ENode(ENode.OPERATOR, this, id, children);
                    107:     };
                    108:     this.operator(id, arity, lbp, rbp, nud, led);
                    109: };
                    110: 
                    111: /**
                    112:  * Returns the defined operator with the given id
                    113:  * @param {string} id - Operator id (text used to recognize it)
                    114:  * @returns {Operator}
                    115:  */
                    116: Definitions.prototype.findOperator = function(id) {
                    117:     for (var i=0; i<this.operators.length; i++) {
                    118:         if (this.operators[i].id == id) {
                    119:             return(this.operators[i]);
                    120:         }
                    121:     }
                    122:     return null;
                    123: }
                    124: 
                    125: /**
1.2     ! damieng   126:  * Returns the ENode for the interval, parsing starting just before the interval separator
        !           127:  * @param {boolean} closed - was the first operator closed ?
        !           128:  * @param {ENode} e1 - First argument (already parsed)
        !           129:  * @param {Operator} op - The operator
        !           130:  * @param {Parser} p - The parser
        !           131:  * @returns {ENode}
        !           132:  */
        !           133: Definitions.prototype.buildInterval = function(closed, e1, op, p) {
        !           134:     p.advance(Definitions.INTERVAL_SEPARATOR);
        !           135:     var e2 = p.expression(0);
        !           136:     if (p.current_token == null || p.current_token.op == null ||
        !           137:             (p.current_token.op.id !== ")" && p.current_token.op.id !== "]")) {
        !           138:         throw new ParseException("Wrong interval syntax.", p.tokens[p.token_nr - 1].from);
        !           139:     }
        !           140:     var interval_type;
        !           141:     if (p.current_token.op.id == ")") {
        !           142:         p.advance(")");
        !           143:         if (closed)
        !           144:             interval_type = ENode.CLOSED_OPEN;
        !           145:         else
        !           146:             interval_type = ENode.OPEN_OPEN;
        !           147:     } else {
        !           148:         p.advance("]");
        !           149:         if (closed)
        !           150:             interval_type = ENode.CLOSED_CLOSED;
        !           151:         else
        !           152:             interval_type = ENode.OPEN_CLOSED;
        !           153:     }
        !           154:     return new ENode(ENode.INTERVAL, op, null, [e1, e2], interval_type);
        !           155: }
        !           156: 
        !           157: /**
1.1       damieng   158:  * Defines all the operators.
                    159:  */
                    160: Definitions.prototype.define = function() {
                    161:     this.suffix("!", 160);
                    162:     this.infix("^", 140, 139);
                    163:     this.infix(".", 130, 129);
                    164:     this.infix("`", 125, 125, function(p, left) {
                    165:         // led (infix operator)
                    166:         // this led for units gathers all the units in an ENode
                    167:         var right = p.expression(125);
                    168:         while (p.current_token != null && "*/".indexOf(p.current_token.value) != -1) {
                    169:             var token2 = p.tokens[p.token_nr];
                    170:             if (token2 == null)
                    171:                 break;
                    172:             if (token2.type != Token.NAME && token2.value != "(")
                    173:                 break;
                    174:             var token3 = p.tokens[p.token_nr+1];
                    175:             if (token3 != null && (token3.value == "(" || token3.type == Token.NUMBER))
                    176:                 break;
                    177:             if (p.unit_mode && p.tokens[p.token_nr].type == Token.NAME) {
                    178:                 var nv = p.tokens[p.token_nr].value;
                    179:                 var cst = false;
                    180:                 for (var i=0; i<p.constants.length; i++) {
                    181:                     if (nv == p.constants[i]) {
                    182:                         cst = true;
                    183:                         break;
                    184:                     }
                    185:                 }
                    186:                 if (cst)
                    187:                     break;
                    188:             }
                    189:             var t = p.current_token;
                    190:             p.advance();
                    191:             right = t.op.led(p, right);
                    192:         }
                    193:         var children = [left, right];
                    194:         return new ENode(ENode.OPERATOR, this, "`", children);
                    195:     });
                    196:     this.infix("*", 120, 120);
                    197:     this.infix("/", 120, 120);
                    198:     this.infix("%", 120, 120);
                    199:     this.infix("+", 100, 100);
                    200:     this.operator("-", Operator.BINARY, 100, 134, function(p) {
                    201:         // nud (prefix operator)
                    202:         var children = [p.expression(134)];
                    203:         return new ENode(ENode.OPERATOR, this, "-", children);
                    204:     }, function(p, left) {
                    205:         // led (infix operator)
                    206:         var children = [left, p.expression(100)];
                    207:         return new ENode(ENode.OPERATOR, this, "-", children);
                    208:     });
                    209:     this.infix("=", 80, 80);
                    210:     this.infix("#", 80, 80);
                    211:     this.infix("<=", 80, 80);
                    212:     this.infix(">=", 80, 80);
                    213:     this.infix("<", 80, 80);
                    214:     this.infix(">", 80, 80);
                    215:     
                    216:     this.separator(")");
                    217:     this.separator(Definitions.ARG_SEPARATOR);
1.2     ! damieng   218:     this.separator(Definitions.INTERVAL_SEPARATOR);
        !           219:     var defs = this;
1.1       damieng   220:     this.operator("(", Operator.BINARY, 200, 200, function(p) {
1.2     ! damieng   221:         // nud (for parenthesis and intervals)
1.1       damieng   222:         var e = p.expression(0);
1.2     ! damieng   223:         if (p.current_token != null && p.current_token.op != null &&
        !           224:                 p.current_token.op.id == Definitions.INTERVAL_SEPARATOR) {
        !           225:             return defs.buildInterval(false, e, this, p);
        !           226:         }
1.1       damieng   227:         p.advance(")");
                    228:         return e;
                    229:     }, function(p, left) {
                    230:         // led (for functions)
                    231:         if (left.type != ENode.NAME && left.type != ENode.SUBSCRIPT)
                    232:             throw new ParseException("Function name expected before a parenthesis.", p.tokens[p.token_nr - 1].from);
                    233:         var children = [left];
                    234:         if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== ")") {
                    235:             while (true) {
                    236:                 children.push(p.expression(0));
                    237:                 if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    238:                     break;
                    239:                 }
                    240:                 p.advance(Definitions.ARG_SEPARATOR);
                    241:             }
                    242:         }
                    243:         p.advance(")");
                    244:         return new ENode(ENode.FUNCTION, this, "(", children);
                    245:     });
                    246:     
                    247:     this.separator("]");
                    248:     this.operator("[", Operator.BINARY, 200, 70, function(p) {
1.2     ! damieng   249:         // nud (for vectors and intervals)
1.1       damieng   250:         var children = [];
                    251:         if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "]") {
1.2     ! damieng   252:             var e = p.expression(0);
        !           253:             if (p.current_token != null && p.current_token.op != null &&
        !           254:                     p.current_token.op.id == Definitions.INTERVAL_SEPARATOR) {
        !           255:                 return defs.buildInterval(true, e, this, p);
        !           256:             }
1.1       damieng   257:             while (true) {
1.2     ! damieng   258:                 children.push(e);
1.1       damieng   259:                 if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    260:                     break;
                    261:                 }
                    262:                 p.advance(Definitions.ARG_SEPARATOR);
1.2     ! damieng   263:                 e = p.expression(0);
1.1       damieng   264:             }
                    265:         }
                    266:         p.advance("]");
                    267:         return new ENode(ENode.VECTOR, this, null, children);
                    268:     }, function(p, left) {
                    269:         // led (for subscript)
                    270:         if (left.type != ENode.NAME && left.type != ENode.SUBSCRIPT)
                    271:             throw new ParseException("Name expected before a square bracket.", p.tokens[p.token_nr - 1].from);
                    272:         var children = [left];
                    273:         if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "]") {
                    274:             while (true) {
                    275:                 children.push(p.expression(0));
                    276:                 if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    277:                     break;
                    278:                 }
                    279:                 p.advance(Definitions.ARG_SEPARATOR);
                    280:             }
                    281:         }
                    282:         p.advance("]");
                    283:         return new ENode(ENode.SUBSCRIPT, this, "[", children);
                    284:     });
1.2     ! damieng   285:     
        !           286:     this.separator("}");
        !           287:     this.prefix("{", 200, function(p) {
        !           288:         // nud (for sets)
        !           289:         var children = [];
        !           290:         if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "}") {
        !           291:             while (true) {
        !           292:                 children.push(p.expression(0));
        !           293:                 if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
        !           294:                     break;
        !           295:                 }
        !           296:                 p.advance(Definitions.ARG_SEPARATOR);
        !           297:             }
        !           298:         }
        !           299:         p.advance("}");
        !           300:         return new ENode(ENode.SET, this, null, children);
        !           301:     });
        !           302:     this.prefix("$", 300, function(p) {
        !           303:         // Perl variables
        !           304:         var e = p.expression(300);
        !           305:         if (e.type != ENode.NAME)
        !           306:             throw new ParseException("Variable name expected after a $.", p.tokens[p.token_nr - 1].from);
        !           307:         e.value = '$' + e.value;
        !           308:         return e;
        !           309:     });
1.1       damieng   310: };
                    311: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>