-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtokens.js
More file actions
227 lines (187 loc) · 6.2 KB
/
tokens.js
File metadata and controls
227 lines (187 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
const readNumberToken = reader => {
let numberText = '';
const numberMatch = /\d/; // Regex for detecing a digit.
// We read until we characters to read.
while (reader.hasNext()) {
if (reader.peek().match(numberMatch)) {
// If a number matches the regex we add the
// character to our string
numberText += reader.peek();
reader.next();
} else {
// if number is not matched we do not need to search anymore.
break;
}
}
if (numberText.length == 0) {
// if no number was detected, return null meaning no token detected.
return null;
}
// We found the token and we return type and value of the token.
return { type: 'number', value: numberText };
}
const readString = reader => {
let value = '';
let startedReading = false; // Flag if we started reading a string
let isEscaping = false; // Flag if we need to ignore the next character.
// We read until we characters to read.
while (reader.hasNext()) {
const matchFound = reader.peek() == "'";
// if we didnt start reading the string and the string character didnt match
// this means that we didn't encounter a string.
if (!startedReading && !matchFound) {
break;
}
// This allow us to have a ' character inside
// our strings as long as we escape it.
if (reader.peek() == '\\' && !isEscaping) {
isEscaping = true;
reader.next();
continue; // we only consume this character and not add it to value.
}
// if we started reading and found a string character,
// this means that we reached the end of string literal
if (startedReading && matchFound && !isEscaping) {
reader.next(); // move to a character after ' in the code.
break;
}
// if we didn't start reading but we found a valid string start
// we set the state for reading the string.
if (!startedReading && matchFound) {
startedReading = true;
reader.next();
continue;
}
// Add the character to our detected string.
value += reader.peek();
reader.next(); // Move the reader to a next character.
isEscaping = false; // Reset escape flag so that we do not escape the next character.
}
if (value.length == 0) {
return null; // if no string token was found
}
// return token of type string
return { type: 'string', value };
}
const readOperator = reader => {
// Regex for operator characters we want to detect.
const operatorMatch = /^(!|\+|-|\*|\/|==|!=|&&|\|\||<|>|<=|>=|=|!=)$/;
// Peek one character to detect one character operator
const oneCharacterOperator = reader.peek();
// Peek one character to detect two characters operator
const twoCharacterOperator = reader.peek(2);
let value = null;
if (twoCharacterOperator.match(operatorMatch)) {
reader.next(2);
value = twoCharacterOperator; // two character operator was found
} else if (oneCharacterOperator.match(operatorMatch)) {
reader.next();
value = oneCharacterOperator; // one character operator was found
}
if (value) {
// Operator is found, we return the token.
return { type: 'operator', value };
}
// Nothing was found so we return null that the token was not found.
return null;
}
const readName = reader => {
let value = '';
const startOfVariableMatch = /[a-z]/;
const restOfVariableMatch = /[a-zA-Z0-9]/;
// If we did not match variable, do not return a token.
if (!reader.peek().match(startOfVariableMatch)) {
return null;
}
value = reader.peek();
reader.next();
while (reader.hasNext() && reader.peek().match(restOfVariableMatch)) {
// add a character to value as long as we match the variable name.
value += reader.peek();
reader.next();
}
// we return a variable token
return { type: 'name', value };
}
const readKeyword = reader => {
if (reader.peek(2).match(/^if$/i)) {
// We detected if keywords and return the token.
reader.next(2);
return { type: 'keyword', value: 'if' };
}
// No keyword detected
return null;
}
const readParentheses = reader => {
if (reader.peek() == '(') {
// We detected '(', start of parentheses
reader.next();
return { type: 'parenStart', value: '(' };
}
if (reader.peek() == ')') {
// We detected ')', end of parentheses
reader.next();
return { type: 'parenEnd', value: ')' };
}
// No token was detected.
return null;
}
const readCodeBlocks = reader => {
if (reader.peek() == '{') {
// We detected '{', start of code block
reader.next();
return { type: 'codeBlockStart' };
}
if (reader.peek() == '}') {
// We detected '}', end of code block
reader.next();
return { type: 'codeBlockEnd' };
}
// No token was detected.
return null;
}
const readEndOfLine = reader => {
if (reader.peek() == ';') {
// Semicolon is detected
reader.next();
return { type: 'endOfLine', value: ';' };
}
// Semicolon is not detected
return null;
}
const readComma = reader => {
if (reader.peek() == ',') {
// Comma was detected
reader.next();
return { type: 'comma', value: ',' };
}
// Token was not detected.
return null;
}
const readWhitespace = reader => {
const whitespaceRegex = /[\t\r\n ]/; // Regex for detecting whitespace.
let value = '';
while (reader.hasNext() && reader.peek().match(whitespaceRegex)) {
// add detected whitespace to the value
value += reader.peek();
reader.next();
}
if (value.length > 0) {
// Return detected whitespace.
return { type: 'whitespace', value };
}
// No whitespace token was detected.
return null;
}
module.exports = [
readNumberToken,
readString,
readOperator,
readKeyword,
readName,
readParentheses,
readCodeBlocks,
readEndOfLine,
readComma,
readWhitespace,
];