Annotation of loncom/html/adm/LC_math_editor/src/tokenizer.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: * String tokenizer. Recognizes only names, numbers, and parser operators.
25: * @constructor
26: * @param {Definitions} defs - Operator definitions
27: * @param {string} text - The text to tokenize
28: */
29: function Tokenizer(defs, text) {
30: this.defs = defs;
31: this.text = text;
32: }
33:
34: /**
35: * Tokenizes the text.
36: * Can throw a ParseException.
37: * @returns {Array.<Token>}
38: */
39: Tokenizer.prototype.tokenize = function() {
40: var c, i, iop, from, tokens, value;
41:
42: i = 0;
43: c = this.text.charAt(i);
44: tokens = [];
45:
46: main:
47: while (c) {
48: from = i;
49:
50: // ignore whitespace
51: if (c <= ' ') {
52: i++;
53: c = this.text.charAt(i);
54: continue;
55: }
56:
57: // check for numbers before operators
58: // (numbers starting with . will not be confused with the . operator)
59: if ((c >= '0' && c <= '9') ||
60: ((c === Definitions.DECIMAL_SIGN_1 || c === Definitions.DECIMAL_SIGN_2) &&
61: (this.text.charAt(i+1) >= '0' && this.text.charAt(i+1) <= '9'))) {
62: value = '';
63:
64: if (c !== Definitions.DECIMAL_SIGN_1 && c !== Definitions.DECIMAL_SIGN_2) {
65: i++;
66: value += c;
67: // Look for more digits.
68: for (;;) {
69: c = this.text.charAt(i);
70: if (c < '0' || c > '9') {
71: break;
72: }
73: i++;
74: value += c;
75: }
76: }
77:
78: // Look for a decimal fraction part.
79: if (c === Definitions.DECIMAL_SIGN_1 || c === Definitions.DECIMAL_SIGN_2) {
80: i++;
81: value += c;
82: for (;;) {
83: c = this.text.charAt(i);
84: if (c < '0' || c > '9') {
85: break;
86: }
87: i += 1;
88: value += c;
89: }
90: }
91:
92: // Look for an exponent part.
93: if (c === 'e' || c === 'E') {
94: i++;
95: value += c;
96: c = this.text.charAt(i);
97: if (c === '-' || c === '+') {
98: i++;
99: value += c;
100: c = this.text.charAt(i);
101: }
102: if (c < '0' || c > '9') {
103: // syntax error in number exponent
104: throw new ParseException("syntax error in number exponent", from, i);
105: }
106: do {
107: i++;
108: value += c;
109: c = this.text.charAt(i);
110: } while (c >= '0' && c <= '9');
111: }
112:
113: /* this is not necessary, as the parser will not recognize the tokens
114: if it is not accepted, and if bad syntax is accepted a * operator will be added
115: // Make sure the next character is not a letter.
116: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
117: // syntax error in number
118: throw new ParseException("syntax error in number", from, i);
119: }
120: */
121:
122: // Convert the string value to a number. If it is finite, then it is a good token.
123: var n = +value.replace(Definitions.DECIMAL_SIGN_1, '.').replace(Definitions.DECIMAL_SIGN_2, '.');
124: if (isFinite(n)) {
125: tokens.push(new Token(Token.NUMBER, from, i - 1, value, null));
126: continue;
127: } else {
128: // syntax error in number
129: throw new ParseException("syntax error in number", from, i);
130: }
131: }
132:
133: // check for operators before names (they could be confused with
134: // variables if they don't use special characters)
135: for (iop = 0; iop < this.defs.operators.length; iop++) {
136: var op = this.defs.operators[iop];
137: if (this.text.substring(i, i+op.id.length) === op.id) {
138: i += op.id.length;
139: c = this.text.charAt(i);
140: tokens.push(new Token(Token.OPERATOR, from, i - 1, op.id, op));
141: continue main;
142: }
143: }
144:
145: // names
1.2 ! damieng 146: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
! 147: (c >= 'α' && c <= 'ω') || (c >= 'Α' && c <= 'Ω') || c == 'µ') {
1.1 damieng 148: value = c;
149: i++;
150: for (;;) {
151: c = this.text.charAt(i);
152: if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1.2 ! damieng 153: (c >= 'α' && c <= 'ω') || (c >= 'Α' && c <= 'Ω') || c == 'µ' ||
1.1 damieng 154: (c >= '0' && c <= '9') || c === '_') {
155: value += c;
156: i++;
157: } else {
158: break;
159: }
160: }
161: tokens.push(new Token(Token.NAME, from, i - 1, value, null));
162: continue;
163: }
164:
165: // unrecognized operator
166: throw new ParseException("unrecognized operator", from, i);
167: }
168: return tokens;
169: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>