Wiki

CodeMirror: cobra.js

File cobra.js, 12.7 KB (added by oahmad04, 10 years ago)
Line 
1// This file was made by modifying the python.js file provided with CodeMirror.
2// python.js comes with the following copyright and license information:
3// CodeMirror, copyright (c) by Marijn Haverbeke and others
4// Distributed under an MIT license: http://codemirror.net/LICENSE
5
6(function(mod) {
7  if (typeof exports == "object" && typeof module == "object") // CommonJS
8    mod(require("../../lib/codemirror"));
9  else if (typeof define == "function" && define.amd) // AMD
10    define(["../../lib/codemirror"], mod);
11  else // Plain browser env
12    mod(CodeMirror);
13})(function(CodeMirror) {
14  "use strict";
15
16  function wordRegexp(words) {
17    return new RegExp("^((" + words.join(")|(") + "))\\b");
18  }
19 
20  var stringQuote = '"';
21  var isAsis = false;
22
23  var commonKeywords = ["adds", "all", "and", "any", "as", "assert", "base", "body", "branch", "callable", "catch", "class", "const", "continue", "cue", "def", "do", "each", "else", "end", "ensure", "enum", "event", "every", "except", "expect", "extend", "finally", "for", "from", "get", "has", "if", "ignore", "implements", "implies", "in", "inherits", "inlined", "inout", "interface", "invariant", "is", "listen", "lock", "mixin", "must", "namespace", "not", "objc", "of", "old", "on", "or", "out", "par", "pass", "passthrough", "post", "print", "pro", "raise", "ref", "require", "result", "return", "same", "set", "sig", "struct", "success", "test", "this", "throw", "to", "trace", "try", "using", "value", "var", "vari", "where", "while", "yield", "continue", "break", "base", "this", "bool", "char", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float", "float32", "float64", "decimal", "number", "type", "use", "assembly"];
24
25  CodeMirror.registerHelper("hintWords", "cobra", commonKeywords);
26
27  function top(state) {
28    return state.scopes[state.scopes.length - 1];
29  }
30
31  CodeMirror.defineMode("cobra", function(conf, parserConf) {
32    var ERRORCLASS = "error";
33
34    var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:=\\.]");
35    var doubleOperators = parserConf.doubleOperators || new RegExp("^((/=)|(<=)|(>=)|(<<)|(>>)|(//))");
36    var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((:\\+)|(:\\-)|(:\\*)|(:%)|(:/)|(:&)|(:\\|)|(:\\^))");
37    var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((://)|(:>>)|(:<<))");
38
39    var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&\\|\\^~<>@]");
40    var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
41
42    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
43
44    var myKeywords = commonKeywords;
45    if(parserConf.extra_keywords != undefined){
46      myKeywords = myKeywords.concat(parserConf.extra_keywords);
47    }
48    var stringPrefixes = new RegExp("^((r|ns|sharp|c)?(['\"]))");
49    var keywords = wordRegexp(myKeywords);
50
51    // tokenizers
52    function tokenBase(stream, state) {
53      // Handle scope changes
54      if (stream.sol() && top(state).type == "cobra") {
55        var scopeOffset = top(state).offset;
56        if (stream.eatSpace()) {
57          var lineOffset = stream.indentation();
58          if (lineOffset > scopeOffset)
59            pushScope(stream, state, "cobra");
60          else if (lineOffset < scopeOffset && dedent(stream, state))
61            state.errorToken = true;
62          return null;
63        } else {
64          var style = tokenBaseInner(stream, state);
65          if (scopeOffset > 0 && dedent(stream, state))
66            style += " " + ERRORCLASS;
67          return style;
68        }
69      }
70      return tokenBaseInner(stream, state);
71    }
72
73    function tokenBaseInner(stream, state) {
74      if (stream.eatSpace()) return null;
75
76      var ch = stream.peek();
77
78      // Handle Comments
79      if (ch == "#") {
80       
81        if (stream.match("/#"))
82          state.tokenize = tokenComment;
83          return tokenComment(stream, state);
84     
85        stream.skipToEnd();
86        return "comment";
87      }
88     
89      // Handle Docstrings
90      if (stream.match('"""')) {
91        state.tokenize = tokenDocComment;
92        return tokenDocComment(stream, state);
93      }
94
95      // Handle Number Literals
96      if (stream.match(/^[0-9\.]/, false)) {
97        var floatLiteral = false;
98        // Floats
99        if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
100        if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
101        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
102        if (floatLiteral) {
103          // Float literals may be "imaginary"
104          stream.eat(/J/i);
105          return "number";
106        }
107        // Integers
108        var intLiteral = false;
109        // Hex
110        if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
111        // Binary
112        if (stream.match(/^0b[01]+/i)) intLiteral = true;
113        // Octal
114        if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
115        // Decimal
116        if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?(?:pi|i|e)?/)) {
117          intLiteral = true;
118        }
119        // Zero by itself with no other piece of number.
120        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
121        if (intLiteral) {
122          // Integer literals may be "long"
123          stream.eat(/L/i);
124          return "number";
125        }
126      }
127     
128      // Handle constants (true, nothing, infinity, etc.)
129      if (stream.match(/\b(?:true|false|nothing|(?:negative)?infinity)\b/)) {
130        return "number";
131      }
132     
133      // Handle complex number constants
134      else if (stream.match(/\[\-?[1-9][0-9\.]*\s+(?:\+|\-)\s+[1-9][0-9\.]*i\]/)) {
135        return "number";
136      }
137
138      // Handle Strings
139      if (stream.match(stringPrefixes)) {
140        state.tokenize = tokenStringFactory(stream.current());
141        return state.tokenize(stream, state);
142      }
143
144      // Handle operators and Delimiters
145      if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
146        return null;
147
148      if (stream.match(doubleOperators)
149          || stream.match(singleOperators))
150        return "operator";
151
152      if (stream.match(singleDelimiters))
153        return null;
154
155      if (stream.match(keywords))
156        return "keyword";
157
158      if (stream.match(/^(self|cls)\b/))
159        return "variable-2";
160
161      if (stream.match(identifiers)) {
162        if (state.lastToken == "def" || state.lastToken == "class"
163          || state.lastToken == "interface" || state.lastToken == "struct"
164          || state.lastToken == "extend" || state.lastToken == "cue"
165          || state.lastToken == "enum"
166          || state.lastToken == "pro" || state.lastToken == "get"
167          || state.lastToken == "set"
168          || state.lastToken == "mixin")
169          return "def";
170        return "variable";
171      }
172
173      // Handle non-detected items
174      stream.next();
175      return null;
176    }
177   
178    function tokenComment(stream, state) {
179      var maybeEnd = false, ch;
180      while (ch = stream.next()) {
181        if (ch == "/" && maybeEnd) {
182          state.tokenize = tokenBase;
183          break;
184        }
185        maybeEnd = (ch == "#");
186      }
187      return "comment";
188    }
189   
190    function tokenDocComment(stream, state) {
191      var maybeEnd = false, ch;
192      var almostDefEnd = false;
193      while (ch = stream.next()) {
194        if (ch == '"' && almostDefEnd) {
195          state.tokenize = tokenBase;
196          break;
197        }
198        almostDefEnd = ((maybeEnd) && (ch == '"'));
199        maybeEnd = (ch == '"');
200      }
201      return "comment";
202    }
203   
204    function tokenStringSub(stream, state) {
205      var count = 1, ch = stream.next();
206      while (ch = stream.next()) {
207        if (ch == "[") count += 1;
208        else if (ch == "]") {
209          count -= 1;
210          if (count == 0) {
211            state.tokenize = tokenStringFactory(stringQuote);
212            return "number";
213          }
214        }
215      }
216    }
217
218    function tokenStringFactory(delimiter) {
219      if (delimiter.match("r") || delimiter.match("c") || delimiter.match("ns")) {
220        isAsis = true;
221      } else {
222        isAsis = false;
223      }
224      while ("rnsharpc".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
225        delimiter = delimiter.substr(1);
226     
227      stringQuote = delimiter;
228
229      var OUTCLASS = "string";
230
231      function tokenString(stream, state) {
232        while (!stream.eol()) {
233          if ((!isAsis) && (stream.peek() == "[")) {
234            state.tokenize = tokenStringSub;
235            return OUTCLASS;
236          }
237          if (!isAsis) {
238            stream.eatWhile(/[^'"\\\[]/);
239          } else {
240            stream.eatWhile(/[^'"\\]/);
241          }
242          if (stream.eat("\\")) {
243            stream.next();
244            if (stream.eol())
245              return OUTCLASS;
246          } else if (stream.match(delimiter)) {
247            state.tokenize = tokenBase;
248            return OUTCLASS;
249          } else {
250            stream.eat(/['"]/);
251          }
252          if (stream.match(delimiter)) {
253            state.tokenize = tokenBase;
254            return OUTCLASS;
255          } else {
256            stream.eat(/['"]/);
257          }
258        }
259        return OUTCLASS;
260      }
261      tokenString.isString = true;
262      return tokenString;
263    }
264
265    function pushScope(stream, state, type) {
266      var offset = 0, align = null;
267      if (type == "cobra") {
268        while (top(state).type != "cobra")
269          state.scopes.pop();
270      }
271      offset = top(state).offset + (type == "cobra" ? conf.indentUnit : hangingIndent);
272      if (type != "cobra" && !stream.match(/^(\s|#.*)*$/, false))
273        align = stream.column() + 1;
274      state.scopes.push({offset: offset, type: type, align: align});
275    }
276
277    function dedent(stream, state) {
278      var indented = stream.indentation();
279      while (top(state).offset > indented) {
280        if (top(state).type != "cobra") return true;
281        state.scopes.pop();
282      }
283      return top(state).offset != indented;
284    }
285
286    function tokenLexer(stream, state) {
287      var style = state.tokenize(stream, state);
288      var current = stream.current();
289
290      // Handle '.' connected identifiers
291      if (current == ".") {
292        style = stream.match(identifiers, false) ? null : null;
293        if (style == null && state.lastStyle == "meta") {
294          // Apply 'meta' style to '.' connected identifiers when
295          // appropriate.
296          style = "meta";
297        }
298        return style;
299      }
300
301      if ((style == "variable" || style == "builtin")
302          && state.lastStyle == "meta")
303        style = "meta";
304
305      // Handle scope changes.
306      if (current == "pass" || current == "return")
307        state.dedent += 1;
308
309      if (current == "do") state.lambda = true;
310      if (current == ":" && !state.lambda && top(state).type == "cobra")
311        pushScope(stream, state, "cobra");
312
313      var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
314      if (delimiter_index != -1)
315        pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
316
317      delimiter_index = "])}".indexOf(current);
318      if (delimiter_index != -1) {
319        if (top(state).type == current) state.scopes.pop();
320        else return ERRORCLASS;
321      }
322      if (state.dedent > 0 && stream.eol() && top(state).type == "cobra") {
323        if (state.scopes.length > 1) state.scopes.pop();
324        state.dedent -= 1;
325      }
326
327      return style;
328    }
329
330    var external = {
331      startState: function(basecolumn) {
332        return {
333          tokenize: tokenBase,
334          scopes: [{offset: basecolumn || 0, type: "cobra", align: null}],
335          lastStyle: null,
336          lastToken: null,
337          lambda: false,
338          dedent: 0
339        };
340      },
341
342      token: function(stream, state) {
343        var addErr = state.errorToken;
344        if (addErr) state.errorToken = false;
345        var style = tokenLexer(stream, state);
346
347        state.lastStyle = style;
348
349        var current = stream.current();
350        if (current && style)
351          state.lastToken = current;
352
353        if (stream.eol() && state.lambda)
354          state.lambda = false;
355        return addErr ? style + " " + ERRORCLASS : style;
356      },
357
358      indent: function(state, textAfter) {
359        if (state.tokenize != tokenBase)
360          return state.tokenize.isString ? CodeMirror.Pass : 0;
361
362        var scope = top(state);
363        var closing = textAfter && textAfter.charAt(0) == scope.type;
364        if (scope.align != null)
365          return scope.align - (closing ? 1 : 0);
366        else if (closing && state.scopes.length > 1)
367          return state.scopes[state.scopes.length - 2].offset;
368        else
369          return scope.offset;
370      },
371     
372      blockCommentStart: "/#",
373      blockCommentEnd: "#/",
374      lineComment: "#",
375      fold: "indent"
376    };
377    return external;
378  });
379
380  CodeMirror.defineMIME("text/x-cobra", "cobra");
381
382  var words = function(str) { return str.split(" "); };
383
384});