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>