tweak lambda 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 /*jslint onevar: false
23  */
24
25 String.prototype.tokens = function (prefix, suffix) {
26     var c;                      // The current character.
27     var from;                   // The index of the start of the token.
28     var i = 0;                  // The index of the current character.
29     var length = this.length;
30     var n;                      // The number value.
31     var q;                      // The quote character.
32     var str;                    // The string value.
33
34     var result = [];            // An array to hold the results.
35
36     var make = function (type, value) {
37
38 // Make a token object.
39
40         return {
41             type: type,
42             value: value,
43             from: from,
44             to: i
45         };
46     };
47
48 // Begin tokenization. If the source string is empty, return nothing.
49
50     if (!this) {
51         return;
52     }
53
54 // If prefix and suffix strings are not provided, supply defaults.
55
56     if (typeof prefix !== 'string') {
57         prefix = '';
58     }
59     if (typeof suffix !== 'string') {
60         suffix = '';
61     }
62
63
64 // Loop through this text, one character at a time.
65
66     c = this.charAt(i);
67     while (c) {
68         from = i;
69
70 // Ignore whitespace.
71
72         if (c <= ' ') {
73             i += 1;
74             c = this.charAt(i);
75
76 // name.
77
78         } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
79             str = c;
80             i += 1;
81             for (;;) {
82                 c = this.charAt(i);
83                 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
84                         (c >= '0' && c <= '9') || c === '_') {
85                     str += c;
86                     i += 1;
87                 } else {
88                     break;
89                 }
90             }
91             result.push(make('name', str));
92
93 // number.
94
95 // A number cannot start with a decimal point. It must start with a digit,
96 // possibly '0'.
97
98         } else if (c >= '0' && c <= '9') {
99             str = c;
100             i += 1;
101
102 // Look for more digits.
103
104             for (;;) {
105                 c = this.charAt(i);
106                 if (c < '0' || c > '9') {
107                     break;
108                 }
109                 i += 1;
110                 str += c;
111             }
112
113 // Make sure the next character is not a letter.
114
115             if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c === '_') {
116                 str += c;
117                 i += 1;
118                 make('number', str).error("Bad number");
119             }
120
121 // Convert the string value to a number. If it is finite, then it is a good
122 // token.
123
124             n = +str;
125             if (isFinite(n)) {
126                 result.push(make('number', n));
127             } else {
128                 make('number', str).error("Bad number");
129             }
130
131 // comment.
132
133         } else if (c === ';') {
134             for (;;) {
135                 c = this.charAt(i);
136                 if (c === '\n' || c === '\r' || c === '') {
137                     break;
138                 }
139                 i += 1;
140             }
141
142 // multi-char operator.
143
144         } else if (prefix.indexOf(c) >= 0) {
145             str = c;
146             i += 1;
147             while (i < length) {
148                 c = this.charAt(i);
149                 if (suffix.indexOf(c) < 0) {
150                     break;
151                 }
152                 str += c;
153                 i += 1;
154             }
155             result.push(make('operator', str));
156
157 // single-character operator.
158
159         } else {
160             i += 1;
161             result.push(make('operator', c));
162             c = this.charAt(i);
163         }
164     }
165     return result;
166 };
167
168