Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/assets/js/codemirror/mode/erlang/erlang.js
1293 views
1
/*jshint unused:true, eqnull:true, curly:true, bitwise:true */
2
/*jshint undef:true, latedef:true, trailing:true */
3
/*global CodeMirror:true */
4
5
// erlang mode.
6
// tokenizer -> token types -> CodeMirror styles
7
// tokenizer maintains a parse stack
8
// indenter uses the parse stack
9
10
// TODO indenter:
11
// bit syntax
12
// old guard/bif/conversion clashes (e.g. "float/1")
13
// type/spec/opaque
14
15
CodeMirror.defineMIME("text/x-erlang", "erlang");
16
17
CodeMirror.defineMode("erlang", function(cmCfg) {
18
"use strict";
19
20
/////////////////////////////////////////////////////////////////////////////
21
// constants
22
23
var typeWords = [
24
"-type", "-spec", "-export_type", "-opaque"];
25
26
var keywordWords = [
27
"after","begin","catch","case","cond","end","fun","if",
28
"let","of","query","receive","try","when"];
29
30
var separatorRE = /[\->,;]/;
31
var separatorWords = [
32
"->",";",","];
33
34
var operatorAtomWords = [
35
"and","andalso","band","bnot","bor","bsl","bsr","bxor",
36
"div","not","or","orelse","rem","xor"];
37
38
var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/;
39
var operatorSymbolWords = [
40
"=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];
41
42
var openParenRE = /[<\(\[\{]/;
43
var openParenWords = [
44
"<<","(","[","{"];
45
46
var closeParenRE = /[>\)\]\}]/;
47
var closeParenWords = [
48
"}","]",")",">>"];
49
50
var guardWords = [
51
"is_atom","is_binary","is_bitstring","is_boolean","is_float",
52
"is_function","is_integer","is_list","is_number","is_pid",
53
"is_port","is_record","is_reference","is_tuple",
54
"atom","binary","bitstring","boolean","function","integer","list",
55
"number","pid","port","record","reference","tuple"];
56
57
var bifWords = [
58
"abs","adler32","adler32_combine","alive","apply","atom_to_binary",
59
"atom_to_list","binary_to_atom","binary_to_existing_atom",
60
"binary_to_list","binary_to_term","bit_size","bitstring_to_list",
61
"byte_size","check_process_code","contact_binary","crc32",
62
"crc32_combine","date","decode_packet","delete_module",
63
"disconnect_node","element","erase","exit","float","float_to_list",
64
"garbage_collect","get","get_keys","group_leader","halt","hd",
65
"integer_to_list","internal_bif","iolist_size","iolist_to_binary",
66
"is_alive","is_atom","is_binary","is_bitstring","is_boolean",
67
"is_float","is_function","is_integer","is_list","is_number","is_pid",
68
"is_port","is_process_alive","is_record","is_reference","is_tuple",
69
"length","link","list_to_atom","list_to_binary","list_to_bitstring",
70
"list_to_existing_atom","list_to_float","list_to_integer",
71
"list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
72
"monitor_node","node","node_link","node_unlink","nodes","notalive",
73
"now","open_port","pid_to_list","port_close","port_command",
74
"port_connect","port_control","pre_loaded","process_flag",
75
"process_info","processes","purge_module","put","register",
76
"registered","round","self","setelement","size","spawn","spawn_link",
77
"spawn_monitor","spawn_opt","split_binary","statistics",
78
"term_to_binary","time","throw","tl","trunc","tuple_size",
79
"tuple_to_list","unlink","unregister","whereis"];
80
81
// upper case: [A-Z] [Ø-Þ] [À-Ö]
82
// lower case: [a-z] [ß-ö] [ø-ÿ]
83
var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/;
84
var escapesRE =
85
/[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/;
86
87
/////////////////////////////////////////////////////////////////////////////
88
// tokenizer
89
90
function tokenizer(stream,state) {
91
// in multi-line string
92
if (state.in_string) {
93
state.in_string = (!doubleQuote(stream));
94
return rval(state,stream,"string");
95
}
96
97
// in multi-line atom
98
if (state.in_atom) {
99
state.in_atom = (!singleQuote(stream));
100
return rval(state,stream,"atom");
101
}
102
103
// whitespace
104
if (stream.eatSpace()) {
105
return rval(state,stream,"whitespace");
106
}
107
108
// attributes and type specs
109
if (!peekToken(state) &&
110
stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) {
111
if (is_member(stream.current(),typeWords)) {
112
return rval(state,stream,"type");
113
}else{
114
return rval(state,stream,"attribute");
115
}
116
}
117
118
var ch = stream.next();
119
120
// comment
121
if (ch == '%') {
122
stream.skipToEnd();
123
return rval(state,stream,"comment");
124
}
125
126
// colon
127
if (ch == ":") {
128
return rval(state,stream,"colon");
129
}
130
131
// macro
132
if (ch == '?') {
133
stream.eatSpace();
134
stream.eatWhile(anumRE);
135
return rval(state,stream,"macro");
136
}
137
138
// record
139
if (ch == "#") {
140
stream.eatSpace();
141
stream.eatWhile(anumRE);
142
return rval(state,stream,"record");
143
}
144
145
// dollar escape
146
if (ch == "$") {
147
if (stream.next() == "\\" && !stream.match(escapesRE)) {
148
return rval(state,stream,"error");
149
}
150
return rval(state,stream,"number");
151
}
152
153
// dot
154
if (ch == ".") {
155
return rval(state,stream,"dot");
156
}
157
158
// quoted atom
159
if (ch == '\'') {
160
if (!(state.in_atom = (!singleQuote(stream)))) {
161
if (stream.match(/\s*\/\s*[0-9]/,false)) {
162
stream.match(/\s*\/\s*[0-9]/,true);
163
return rval(state,stream,"fun"); // 'f'/0 style fun
164
}
165
if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) {
166
return rval(state,stream,"function");
167
}
168
}
169
return rval(state,stream,"atom");
170
}
171
172
// string
173
if (ch == '"') {
174
state.in_string = (!doubleQuote(stream));
175
return rval(state,stream,"string");
176
}
177
178
// variable
179
if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) {
180
stream.eatWhile(anumRE);
181
return rval(state,stream,"variable");
182
}
183
184
// atom/keyword/BIF/function
185
if (/[a-z_ß-öø-ÿ]/.test(ch)) {
186
stream.eatWhile(anumRE);
187
188
if (stream.match(/\s*\/\s*[0-9]/,false)) {
189
stream.match(/\s*\/\s*[0-9]/,true);
190
return rval(state,stream,"fun"); // f/0 style fun
191
}
192
193
var w = stream.current();
194
195
if (is_member(w,keywordWords)) {
196
return rval(state,stream,"keyword");
197
}else if (is_member(w,operatorAtomWords)) {
198
return rval(state,stream,"operator");
199
}else if (stream.match(/\s*\(/,false)) {
200
// 'put' and 'erlang:put' are bifs, 'foo:put' is not
201
if (is_member(w,bifWords) &&
202
((peekToken(state).token != ":") ||
203
(peekToken(state,2).token == "erlang"))) {
204
return rval(state,stream,"builtin");
205
}else if (is_member(w,guardWords)) {
206
return rval(state,stream,"guard");
207
}else{
208
return rval(state,stream,"function");
209
}
210
}else if (is_member(w,operatorAtomWords)) {
211
return rval(state,stream,"operator");
212
}else if (lookahead(stream) == ":") {
213
if (w == "erlang") {
214
return rval(state,stream,"builtin");
215
} else {
216
return rval(state,stream,"function");
217
}
218
}else if (is_member(w,["true","false"])) {
219
return rval(state,stream,"boolean");
220
}else if (is_member(w,["true","false"])) {
221
return rval(state,stream,"boolean");
222
}else{
223
return rval(state,stream,"atom");
224
}
225
}
226
227
// number
228
var digitRE = /[0-9]/;
229
var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int
230
if (digitRE.test(ch)) {
231
stream.eatWhile(digitRE);
232
if (stream.eat('#')) { // 36#aZ style integer
233
if (!stream.eatWhile(radixRE)) {
234
stream.backUp(1); //"36#" - syntax error
235
}
236
} else if (stream.eat('.')) { // float
237
if (!stream.eatWhile(digitRE)) {
238
stream.backUp(1); // "3." - probably end of function
239
} else {
240
if (stream.eat(/[eE]/)) { // float with exponent
241
if (stream.eat(/[-+]/)) {
242
if (!stream.eatWhile(digitRE)) {
243
stream.backUp(2); // "2e-" - syntax error
244
}
245
} else {
246
if (!stream.eatWhile(digitRE)) {
247
stream.backUp(1); // "2e" - syntax error
248
}
249
}
250
}
251
}
252
}
253
return rval(state,stream,"number"); // normal integer
254
}
255
256
// open parens
257
if (nongreedy(stream,openParenRE,openParenWords)) {
258
return rval(state,stream,"open_paren");
259
}
260
261
// close parens
262
if (nongreedy(stream,closeParenRE,closeParenWords)) {
263
return rval(state,stream,"close_paren");
264
}
265
266
// separators
267
if (greedy(stream,separatorRE,separatorWords)) {
268
return rval(state,stream,"separator");
269
}
270
271
// operators
272
if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {
273
return rval(state,stream,"operator");
274
}
275
276
return rval(state,stream,null);
277
}
278
279
/////////////////////////////////////////////////////////////////////////////
280
// utilities
281
function nongreedy(stream,re,words) {
282
if (stream.current().length == 1 && re.test(stream.current())) {
283
stream.backUp(1);
284
while (re.test(stream.peek())) {
285
stream.next();
286
if (is_member(stream.current(),words)) {
287
return true;
288
}
289
}
290
stream.backUp(stream.current().length-1);
291
}
292
return false;
293
}
294
295
function greedy(stream,re,words) {
296
if (stream.current().length == 1 && re.test(stream.current())) {
297
while (re.test(stream.peek())) {
298
stream.next();
299
}
300
while (0 < stream.current().length) {
301
if (is_member(stream.current(),words)) {
302
return true;
303
}else{
304
stream.backUp(1);
305
}
306
}
307
stream.next();
308
}
309
return false;
310
}
311
312
function doubleQuote(stream) {
313
return quote(stream, '"', '\\');
314
}
315
316
function singleQuote(stream) {
317
return quote(stream,'\'','\\');
318
}
319
320
function quote(stream,quoteChar,escapeChar) {
321
while (!stream.eol()) {
322
var ch = stream.next();
323
if (ch == quoteChar) {
324
return true;
325
}else if (ch == escapeChar) {
326
stream.next();
327
}
328
}
329
return false;
330
}
331
332
function lookahead(stream) {
333
var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false);
334
return m ? m.pop() : "";
335
}
336
337
function is_member(element,list) {
338
return (-1 < list.indexOf(element));
339
}
340
341
function rval(state,stream,type) {
342
343
// parse stack
344
pushToken(state,realToken(type,stream));
345
346
// map erlang token type to CodeMirror style class
347
// erlang -> CodeMirror tag
348
switch (type) {
349
case "atom": return "atom";
350
case "attribute": return "attribute";
351
case "boolean": return "special";
352
case "builtin": return "builtin";
353
case "close_paren": return null;
354
case "colon": return null;
355
case "comment": return "comment";
356
case "dot": return null;
357
case "error": return "error";
358
case "fun": return "meta";
359
case "function": return "tag";
360
case "guard": return "property";
361
case "keyword": return "keyword";
362
case "macro": return "variable-2";
363
case "number": return "number";
364
case "open_paren": return null;
365
case "operator": return "operator";
366
case "record": return "bracket";
367
case "separator": return null;
368
case "string": return "string";
369
case "type": return "def";
370
case "variable": return "variable";
371
default: return null;
372
}
373
}
374
375
function aToken(tok,col,ind,typ) {
376
return {token: tok,
377
column: col,
378
indent: ind,
379
type: typ};
380
}
381
382
function realToken(type,stream) {
383
return aToken(stream.current(),
384
stream.column(),
385
stream.indentation(),
386
type);
387
}
388
389
function fakeToken(type) {
390
return aToken(type,0,0,type);
391
}
392
393
function peekToken(state,depth) {
394
var len = state.tokenStack.length;
395
var dep = (depth ? depth : 1);
396
397
if (len < dep) {
398
return false;
399
}else{
400
return state.tokenStack[len-dep];
401
}
402
}
403
404
function pushToken(state,token) {
405
406
if (!(token.type == "comment" || token.type == "whitespace")) {
407
state.tokenStack = maybe_drop_pre(state.tokenStack,token);
408
state.tokenStack = maybe_drop_post(state.tokenStack);
409
}
410
}
411
412
function maybe_drop_pre(s,token) {
413
var last = s.length-1;
414
415
if (0 < last && s[last].type === "record" && token.type === "dot") {
416
s.pop();
417
}else if (0 < last && s[last].type === "group") {
418
s.pop();
419
s.push(token);
420
}else{
421
s.push(token);
422
}
423
return s;
424
}
425
426
function maybe_drop_post(s) {
427
var last = s.length-1;
428
429
if (s[last].type === "dot") {
430
return [];
431
}
432
if (s[last].type === "fun" && s[last-1].token === "fun") {
433
return s.slice(0,last-1);
434
}
435
switch (s[s.length-1].token) {
436
case "}": return d(s,{g:["{"]});
437
case "]": return d(s,{i:["["]});
438
case ")": return d(s,{i:["("]});
439
case ">>": return d(s,{i:["<<"]});
440
case "end": return d(s,{i:["begin","case","fun","if","receive","try"]});
441
case ",": return d(s,{e:["begin","try","when","->",
442
",","(","[","{","<<"]});
443
case "->": return d(s,{r:["when"],
444
m:["try","if","case","receive"]});
445
case ";": return d(s,{E:["case","fun","if","receive","try","when"]});
446
case "catch":return d(s,{e:["try"]});
447
case "of": return d(s,{e:["case"]});
448
case "after":return d(s,{e:["receive","try"]});
449
default: return s;
450
}
451
}
452
453
function d(stack,tt) {
454
// stack is a stack of Token objects.
455
// tt is an object; {type:tokens}
456
// type is a char, tokens is a list of token strings.
457
// The function returns (possibly truncated) stack.
458
// It will descend the stack, looking for a Token such that Token.token
459
// is a member of tokens. If it does not find that, it will normally (but
460
// see "E" below) return stack. If it does find a match, it will remove
461
// all the Tokens between the top and the matched Token.
462
// If type is "m", that is all it does.
463
// If type is "i", it will also remove the matched Token and the top Token.
464
// If type is "g", like "i", but add a fake "group" token at the top.
465
// If type is "r", it will remove the matched Token, but not the top Token.
466
// If type is "e", it will keep the matched Token but not the top Token.
467
// If type is "E", it behaves as for type "e", except if there is no match,
468
// in which case it will return an empty stack.
469
470
for (var type in tt) {
471
var len = stack.length-1;
472
var tokens = tt[type];
473
for (var i = len-1; -1 < i ; i--) {
474
if (is_member(stack[i].token,tokens)) {
475
var ss = stack.slice(0,i);
476
switch (type) {
477
case "m": return ss.concat(stack[i]).concat(stack[len]);
478
case "r": return ss.concat(stack[len]);
479
case "i": return ss;
480
case "g": return ss.concat(fakeToken("group"));
481
case "E": return ss.concat(stack[i]);
482
case "e": return ss.concat(stack[i]);
483
}
484
}
485
}
486
}
487
return (type == "E" ? [] : stack);
488
}
489
490
/////////////////////////////////////////////////////////////////////////////
491
// indenter
492
493
function indenter(state,textAfter) {
494
var t;
495
var unit = cmCfg.indentUnit;
496
var wordAfter = wordafter(textAfter);
497
var currT = peekToken(state,1);
498
var prevT = peekToken(state,2);
499
500
if (state.in_string || state.in_atom) {
501
return CodeMirror.Pass;
502
}else if (!prevT) {
503
return 0;
504
}else if (currT.token == "when") {
505
return currT.column+unit;
506
}else if (wordAfter === "when" && prevT.type === "function") {
507
return prevT.indent+unit;
508
}else if (wordAfter === "(" && currT.token === "fun") {
509
return currT.column+3;
510
}else if (wordAfter === "catch" && (t = getToken(state,["try"]))) {
511
return t.column;
512
}else if (is_member(wordAfter,["end","after","of"])) {
513
t = getToken(state,["begin","case","fun","if","receive","try"]);
514
return t ? t.column : CodeMirror.Pass;
515
}else if (is_member(wordAfter,closeParenWords)) {
516
t = getToken(state,openParenWords);
517
return t ? t.column : CodeMirror.Pass;
518
}else if (is_member(currT.token,[",","|","||"]) ||
519
is_member(wordAfter,[",","|","||"])) {
520
t = postcommaToken(state);
521
return t ? t.column+t.token.length : unit;
522
}else if (currT.token == "->") {
523
if (is_member(prevT.token, ["receive","case","if","try"])) {
524
return prevT.column+unit+unit;
525
}else{
526
return prevT.column+unit;
527
}
528
}else if (is_member(currT.token,openParenWords)) {
529
return currT.column+currT.token.length;
530
}else{
531
t = defaultToken(state);
532
return truthy(t) ? t.column+unit : 0;
533
}
534
}
535
536
function wordafter(str) {
537
var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/);
538
539
return truthy(m) && (m.index === 0) ? m[0] : "";
540
}
541
542
function postcommaToken(state) {
543
var objs = state.tokenStack.slice(0,-1);
544
var i = getTokenIndex(objs,"type",["open_paren"]);
545
546
return truthy(objs[i]) ? objs[i] : false;
547
}
548
549
function defaultToken(state) {
550
var objs = state.tokenStack;
551
var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]);
552
var oper = getTokenIndex(objs,"type",["operator"]);
553
554
if (truthy(stop) && truthy(oper) && stop < oper) {
555
return objs[stop+1];
556
} else if (truthy(stop)) {
557
return objs[stop];
558
} else {
559
return false;
560
}
561
}
562
563
function getToken(state,tokens) {
564
var objs = state.tokenStack;
565
var i = getTokenIndex(objs,"token",tokens);
566
567
return truthy(objs[i]) ? objs[i] : false;
568
}
569
570
function getTokenIndex(objs,propname,propvals) {
571
572
for (var i = objs.length-1; -1 < i ; i--) {
573
if (is_member(objs[i][propname],propvals)) {
574
return i;
575
}
576
}
577
return false;
578
}
579
580
function truthy(x) {
581
return (x !== false) && (x != null);
582
}
583
584
/////////////////////////////////////////////////////////////////////////////
585
// this object defines the mode
586
587
return {
588
startState:
589
function() {
590
return {tokenStack: [],
591
in_string: false,
592
in_atom: false};
593
},
594
595
token:
596
function(stream, state) {
597
return tokenizer(stream, state);
598
},
599
600
indent:
601
function(state, textAfter) {
602
return indenter(state,textAfter);
603
},
604
605
lineComment: "%"
606
};
607
});
608
609