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