shift over to new parser and evaluator
[lambda.git] / code / tokens.js
1 // Based on tokens.js
2 //              2009-05-17
3 //              (c) 2006 Douglas Crockford
4
5 //              Produce an array of simple token objects from a string.
6 //              A simple token object contains these members:
7 //                   type: 'name', 'string', 'number', 'operator'
8 //                   value: string or number value of the token
9 //                   from: index of first character of the token
10 //                   to: index of the last character + 1
11
12 //              Comments of the ; type are ignored.
13
14 //              Operators are by default single characters. Multicharacter
15 //              operators can be made by supplying a string of prefix and
16 //              suffix characters.
17 //              characters. For example,
18 //                   '<>+-&', '=>&:'
19 //              will match any of these:
20 //                   <=  >>  >>>  <>  >=  +: -: &: &&: &&
21
22
23
24 String.prototype.tokens = function (prefix, suffix) {
25     var c;                      // The current character.
26     var from;                   // The index of the start of the token.
27     var i = 0;                  // The index of the current character.
28     var length = this.length;
29     var n;                      // The number value.
30     var q;                      // The quote character.
31     var str;                    // The string value.
32
33     var result = [];            // An array to hold the results.
34
35     var make = function (type, value) {
36
37 // Make a token object.
38
39         return {
40             type: type,
41             value: value,
42             from: from,
43             to: i
44         };
45     };
46
47 // Begin tokenization. If the source string is empty, return nothing.
48
49     if (!this) {
50         return;
51     }
52
53 // If prefix and suffix strings are not provided, supply defaults.
54
55     if (typeof prefix !== 'string') {
56                 prefix = '';
57     }
58     if (typeof suffix !== 'string') {
59                 suffix = '';
60     }
61
62
63 // Loop through this text, one character at a time.
64
65     c = this.charAt(i);
66     while (c) {
67         from = i;
68
69 // Ignore whitespace.
70
71         if (c <= ' ') {
72             i += 1;
73             c = this.charAt(i);
74
75 // name.
76
77         } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
78             str = c;
79             i += 1;
80             for (;;) {
81                 c = this.charAt(i);
82                 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
83                         (c >= '0' && c <= '9') || c === '_') {
84                     str += c;
85                     i += 1;
86                 } else {
87                     break;
88                 }
89             }
90             result.push(make('name', str));
91
92 // number.
93
94 // A number cannot start with a decimal point. It must start with a digit,
95 // possibly '0'.
96
97         } else if (c >= '0' && c <= '9') {
98             str = c;
99             i += 1;
100
101 // Look for more digits.
102
103             for (;;) {
104                 c = this.charAt(i);
105                 if (c < '0' || c > '9') {
106                     break;
107                 }
108                 i += 1;
109                 str += c;
110             }
111
112 // Make sure the next character is not a letter.
113
114             if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_') {
115                 str += c;
116                 i += 1;
117                 make('number', str).error("Bad number");
118             }
119
120 // Convert the string value to a number. If it is finite, then it is a good
121 // token.
122
123             n = +str;
124             if (isFinite(n)) {
125                 result.push(make('number', n));
126             } else {
127                 make('number', str).error("Bad number");
128             }
129
130 // comment.
131
132                 } else if (c === ';') {
133             for (;;) {
134                 c = this.charAt(i);
135                 if (c === '\n' || c === '\r' || c === '') {
136                     break;
137                 }
138                 i += 1;
139             }
140
141 // multi-char operator.
142
143         } else if (prefix.indexOf(c) >= 0) {
144             str = c;
145             i += 1;
146             while (i < length) {
147                 c = this.charAt(i);
148                 if (suffix.indexOf(c) < 0) {
149                     break;
150                 }
151                 str += c;
152                 i += 1;
153             }
154             result.push(make('operator', str));
155
156 // single-character operator.
157
158         } else {
159             i += 1;
160             result.push(make('operator', c));
161             c = this.charAt(i);
162         }
163     }
164     return result;
165 };
166
167